home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Medal Software 2
/
Gold Medal Software Volume 2 (Gold Medal) (1994).iso
/
drivers
/
ansi_j.arj
/
ANSI.ASM
next >
Wrap
Assembly Source File
|
1993-05-15
|
125KB
|
2,255 lines
;------------------------------------------------------------------------;
; ANSI.COM - Replacement for the ANSI.SYS console device driver. ;
; Unlike ANSI.SYS which must be installed at boot time, ANSI.COM ;
; can be installed and uninstalled at anytime. Enhancements include ;
; a fast screen write and variable sized keyboard reassignment buffer. ;
; ;
; Update 3/2/89 - Fix for DOS function 0Bh, Check Standard Input Status ;
; and STDIN in ANSI_INT_21 handler. ;
; Leading zero inserted for Device Status Report for ;
; single digit cursor positions. ;
; INFORMATION typo 40 for 40H. ;
; WRITE_FAST modified to handle CR and LF instead of ;
; WRITE_CHAR. ;
; Update 3/7/89 - Fix for CLS in graphics mode. Version 1.2 ;
; ;
; Update 8/8/89 - STI added to INT 21 and 29 handler. Version 1.3 ;
;: ;
;: Update 01/01/90 - [KON | KOFF] and [PON | POFF] added Version 1.31 ;
;: (all changes marked with ;: - Gary Meeker) ;
;| Update 03/23/90 - /Q option added for no output when exectued 1.32 ;
;| (all changes marked with ;| - Gary Meeker) ;
;; Update 05/16/90 - removed Syntax from output unless needed 1.33 ;
;; added /T test for load. ;
;; (all changes marked with ;; - Gary Meeker) ;
;. Update 10/28/90 - Added /P nn top of screen protection mode 1.34 ;
;. (all changes marked with ;. - Gary Meeker) ;
;@ Update 12/01/90 - Added PCBoard @X## color code support 1.35 ;
;@ also added @Variable@ Support ;
;@ added /S to load stats data from file ;
;@ (all changes marked with ;@ - Gary Meeker) ;
;& ;
;& Update 12/12/90 - My mistake, I didn't realize @X00 saved 1.36 ;
;& the color and @XFF restored it. Works now. ;
;& Also made prompts return to start of line ;
;& when cleared (like PCBoard does) & QON ;
;& now changes @MORE@ & @PAUSE@ into @WAIT@ ;
;& (all changes marked with ;& - Gary Meeker) ;
; ;
;$ Update 4/22/91 - Change set cursor routine. If new position 1.37 ;
;$ is off the screen use the last row or column. ;
;$ Added switch to not include PCB code. ;
;$ Corrected syntax display. ;
;$ Modified CLS to honor protected (/P) lines. ;
;$ Modified /T to returned status switches. ;
;$ Modified /P to accept * to mean current row-1. ;
;$ (all changes marked with ;$ - Wayne Mingee) ;
; ;
;+ Update 9/19/91 - Allow ESC[p to clear all KB reassigns and 1.38 ;
;+ ESC[np to clear single key reassign. ;
;+ ie: ESC[65p will reset ascii 65 to it's default [A] ;
; ;
;^ Update 01/01/92 - Added XON/XOFF to control @X## color codes 1.39 ;
;^ added support for finding relocated copies. ;
;^ This should allow LOADHI under DOS 5 or QRAM/QEMM ;
;^ (all changes marked with ;^ - Gary Meeker) ;
;^ (Was added to my 1.37 version 01/09/91 but now ;
;^ incorporated into this version as 1.39 to include ;
;^ changes by Wayne Mingee) ;
; ;
;~ Update 05/15/93 - Added additional support for PCBoard 15.0 1.3j ;
;~ @Variable:xxx@ and new variables. ;
;~ (all changes marked with ;~ - Gary Meeker) ;
; ;
; PC Magazine - Michael J. Mefford ;
;------------------------------------------------------------------------;
PCB equ 0 ;$ 1 = if PCB code wanted
_TEXT SEGMENT PUBLIC 'CODE'
ASSUME CS:_TEXT,DS:_TEXT,ES:_TEXT,SS:_TEXT
ORG 100H
START: JMP INITIALIZE
; DATA AREA
SIGNATURE DB CR,SPACE,SPACE,SPACE,CR,LF
COPYRIGHT DB 'ANSI 1.3j (C) 1988 Ziff Communications Co.',CR,LF ;^;~
PROGRAMMER DB 'PC Magazine ',BOX,' Michael J. Mefford',CR,LF,LF,'$'
DB CTRL_Z
CR EQU 13
LF EQU 10
CTRL_Z EQU 26
SPACE EQU 32
BOX EQU 254
ESC_CHAR EQU 27
SINGLE_QUOTE EQU 39
DOUBLE_QUOTE EQU 34
BELL EQU 7
BS EQU 8
TAB EQU 9
OFF EQU 1
ON EQU 2
SLOW EQU 4
FAST EQU 8
KOFF EQU 16 ;:
KON EQU 32 ;:
POFF EQU 64 ;:
PON EQU 128 ;:
XOFF EQU 256 ;^
XON EQU 512 ;^
STATUS_MASK EQU 1111110000000001B ;: Set Mask and Bit
AND_MASK EQU 1111111111111100B ;^ Set Mask
OR_MASK EQU 0000000000000001B ;^ Set Bit
ANSI_STATE DW ESC_STATE
STATUS DW ON OR FAST OR KON OR PON OR XON ;: ;^
PARAMETERS DB 'OFF',0,'ON',0,0,'SLOWFASTKOFFKON',0,'POFFPON',0 ;:
DB 'XOFFXON',0 ;^
LAST_PARAMETER EQU $ - PARAMETERS
multiplex_id db 0dbh ;^ Program ID for multiplex int
dos_version dw 0 ;^ DOS Version number
int2fh dd -1 ;^ Int 2f vector (DOS MULTIPLEX)
OLD_INT_29 DW ?,?
OLD_INT_16 DW ?,?
OLD_INT_21 DW ?,?
if PCB ;$
OLD_INT_08 DW ?,? ;@ Save Timer Interupt
endif ;$
ATTRIBUTE DB 7
if PCB ;$
SAVE_ATTRIBUTE DB ? ;& For saving attribute
endif ;$
SAVE_POSITION DW 0
LINE_WRAP DB ON
QUOTE_TYPE DB ?
ESC_COUNT DW 0
NUMBER_COUNT DW 0
if PCB ;~
IGNORE DW 0 ;~ Count of characters to ignore
JUSTIFY DB 'L' ;~ Justification - Default L
endif ;~
COMMAND_TABLE LABEL BYTE
DB 'H', 'A', 'B', 'C', 'D', 'f', 'n', 's', 'u', 'K', 'm', 'h', 'l', 'p', 'J'
COMMAND_LENGTH EQU $ - COMMAND_TABLE
DW CURS_POSITION, CURSOR_UP, CURSOR_DOWN, CURS_FORWARD, CURS_BACKWARD
DW HORZ_VERT_POS, DEVICE_STATUS, SAVE_CURSOR, RESTORE_CURS, ERASE_IN_LINE
DW SGR, SET_MODE, RESET_MODE, REASSIGNMENT, CLS
COMMAND_END EQU $ - 2
ATTRIBUTE_TABLE LABEL BYTE
DB 00,01,04,05,07,08,30,31,32,33,34,35,36,37,40,41,42,43,44,45,46,47
ATTRIBUTE_LENGTH EQU $ - ATTRIBUTE_TABLE
;Format: AND mask,OR mask
DB 000H,07H, 0FFH,08H, 0F8H,01H, 0FFH,80H, 0F8H,70H, 088H,00H
DB 0F8H,00H, 0F8H,04H, 0F8H,02H, 0F8H,06H, 0F8H,01H, 0F8H,05H
DB 0F8H,03H, 0F8H,07H, 08FH,00H, 08FH,40H, 08FH,20H, 08FH,60H
DB 08FH,10H, 08FH,50H, 08FH,30H, 08FH,70H
ATTRIBUTE_END EQU $ - 2
if PCB ;$
AT_VARIABLE_TABLE LABEL BYTE ;@
DB '@CLS@CLREOL@AUTOMORE@POFF@PON@QOFF@QON@BEEP@MORE@PAUSE@WAIT@HANGUP@' ;@
DB 'DELAY@POS@' ;~ For PCBoard 15.0
DB 'OPTEXT@FIRSTU@FIRST@USER@CITY@HOMEPHONE@DATAPHONE@PROLTR@PRODESC@' ;@
DB 'EXPDATE@LASTDATEON@LASTTIMEON@INCONF@CONFNAME@BOARDNAME@' ;@
DB 'LASTCALLERNODE@LASTCALLERSYSTEM@EVENT@SYSOPIN@SYSOPOUT@BPS@NODE@' ;@
DB 'OFFHOURS@CARRIER@BICPS@RCPS@SCPS@' ;~ For PCBoard 15.0
DB 'WHO@' ;~
DB 'SYSDATE@SYSTIME@FILERATIO@BYTERATIO@' ;@
DB 'SECURITY@NUMCALLS@NUMTIMESON@TIMELEFT@TIMELIMIT@TIMEUSED@TOTALTIME@' ;@
DB 'BYTESLEFT@BYTELIMIT@DLFILES@UPFILES@KBLEFT@KBLIMIT@CONFNUM@' ;@
DB 'CURMSGNUM@HIGHMSGNUM@LOWMSGNUM@MSGREAD@MSGLEFT@NUMBLT@NUMDIR@' ;@
DB 'DAYBYTES@MINLEFT@DLBYTES@UPBYTES@EXPDAYS@' ;@
DB 'LMR@FREESPACE@RFILES@RBYTES@SFILES@SBYTES@' ;~
LAST_VARIABLE EQU $ - 1 ;@
JUMP_TABLE LABEL WORD ;@
DW CLS, ERASE_IN_LINE, AUTOMORE, AUTOOFF, AUTOOFF, QOFF, QON ;&;@
DW BEEP, MORE, PAUSE, WAIT1, SKIP_IT ;@
DW DELAY_IT, POS_IT ;~
ACTION_CODES EQU 14 ;~ Length of Table
STRING_CODES EQU ACTION_CODES+28 ;~
SUB_TABLE LABEL BYTE ;@
DB 8,15,15,25,24,13,13,1,47 ;@
DB 8,8,5,33,13,63 ;@
DB 52,52,5,5,5,6,2 ;~ ;@
DB 12,6,6,6,6,255 ;~
DB 0,0,4,4 ;@
DB 2,4,2,2,2,2,2 ;@
DB 4,4,2,2,4,4,2 ;@
DB 4,4,4,4,4,2,2 ;@
DB 4,2,4,4,2 ;@
DB 4,4,2,4,2,4 ;~
SUB_LIST LABEL BYTE ;@
DB '!OPTEXT!' ;@ OPTTEXT
DB 'GARY',0,0,0,0,0,0,0,0,0,0,0 ;@ FirstU
DB 'Gary',0,0,0,0,0,0,0,0,0,0,0 ;@ First
DB 'GARY MEEKER',0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;@ User
DB 'LAWRENCEVILLE, GA',0,0,0,0,0,0,0 ;@ City
DB '404 995-2699',0 ;@ HomePhone
DB '404 962-1788',0 ;@ DataPhone
DB 'Z' ;@ Proltr
DB 'Zmodem (Batch U/L and D/L)', 15 DUP (0) ;@ ProDesc
DB '01-01-91' ;@ ExpDate
DB '12-01-90' ;@ LastDateOn
DB '08:00' ;@ LastTimeOn
DB 'Sysop (19) Conference ',0,0,0,0,0,0,0,0,0,0,0 ;@ InConf
DB 'Sysop',0,0,0,0,0,0,0,0 ;@ ConfName
DB 'SHARP Technical Support Line BBS', 31 DUP (0) ;@ BoardName
DB 'MIKE BATE (RIVERDALE, GA)', 27 DUP (0) ;@ LastCallerNode
DB 'GARY MEEKER (LAWRENCEVILLE, GA)', 21 DUP (0) ;@ LastCallerSystem
DB '04:30' ;@ Event
DB '08:30' ;@ SysopIn
DB '17:00' ;@ SysopOut
DB '2400',0,0 ;@ bps ;~
DB '1',0 ;@ Node
DB '00:00-23:59',0 ;~ Offhours
DB '38400',0 ;~ Carrier
DB '3500',0,0 ;~ BICPS
DB '1700',0,0 ;~ RCPS
DB '1800',0,0 ;~ SCPS
DB 10,13 ;~ Who
DB ' (#) Status User',10,13 ;~
DB ' --- --------------------- -----------------------------',10,13 ;~
DB ' 1 Available for CHAT GARY MEEKER (LAWRENCEVILLE, GA)',10,13 ;~
DB ' 2 No Caller this Node ',10,13 ;~
DB 26 DUP (0) ;~
DD 48 ;@ FileRatio
DD 128 ;@ ByteRatio
DW 120 ;@ Security
DD 23946 ;@ numcalls
DW 2107 ;@ numtimeson
DW 61 ;@ timeleft
DW 121 ;@ timelimit
DW 59 ;@ timeused
DW 59 ;@ totaltime
DD 9508313 ;@ bytesleft
DD 10238976 ;@ bytelimit
DW 44 ;@ dlfiles
DW 1234 ;@ upfiles
DD 9285 ;@ KBLeft
DD 9999 ;@ KBLimit
DW 19 ;@ ConfNum
DD 4734 ;@ CurMsgNum
DD 4745 ;@ HighMsgNum
DD 2 ;@ LowMsgNum
DD 3456 ;@ MsgRead
DD 987 ;@ MsgLeft
DW 20 ;@ NumBlt
DW 31 ;@ NumDir
DD 30663 ;@ DayBytes
DW 61 ;@ MinLeft
DD 730663 ;@ DLBytes
DD 12345678 ;@ UpBytes
DW 0 ;@ ExpDays
DD 4700 ;~ LMR
DD 49356800 ;~ FreeSpace
DW 8 ;~ RFiles
DD 234567 ;~ RBytes
DW 31 ;~ SFiles
DD 3456789 ;~ SBytes
SUB_LENGTH EQU $ - SUB_LIST
PAUSE_TIMER DW 182 ;@
PAUSE_DELAY DW 182 ;@ 10 Seconds
MORE_DELAY DW 182 * 12 ;@ 2 Minute
WAIT_DELAY DW 182 * 12 ;@ 2 Minute
WAIT_PROMPT DB 'press enter to continue' ;@
WAIT_LENGTH DW $ - WAIT_PROMPT ;@
MORE_PROMPT DB '(61 min left), (H)elp, More?' ;@
MORE_LENGTH DW $ - MORE_PROMPT ;@
COMMA DB 0 ;@
Q_STATE DB 0 ;&
endif ;$
BIOS_ACTIVE_PAGE EQU 62H
ACTIVE_PAGE DB ?
ADDR_6845 DW ?
BIOS_CRT_MODE EQU 49H
CRT_MODE DB ?
CRT_COLS DW ?
CRT_LEN DW ?
CRT_START DW ?
CRT_DATA_LENGTH EQU $ - CRT_MODE
CURSOR_POSN LABEL WORD
CURSOR_COL DB ?
CURSOR_ROW DB ?
CRT_ROWS DB ?
START_SCROLL DB 0 ;. Number of rows to protect from scrolling up
DOS_INPUT DB OFF
BUSY_FLAG DB OFF
REASSIGN_FLAG DB OFF
REMOVE_FLAG DB ON
REASSIGN_COUNT DW 0
REASSIGN_POS DW ?
FUNCTION_16 DB ?
ZR EQU 1000000B
ESC_BUFFER_SIZE EQU 126
REASSIGNMENT_DEFAULT EQU 200
REASSIGNMENT_SIZE DW REASSIGNMENT_DEFAULT
REASSIGNMENT_MAX EQU 60 * 1024
REASSIGN_END DW REASSIGNMENT_BUFFER
BUFFER_END DW REASSIGNMENT_BUFFER + REASSIGNMENT_DEFAULT
; CODE AREA
;************* INTERRUPT HANDLERS *************;
;------------------------------------------------------------------------------;
; INT 29 is an undocumented interrupt called by DOS for output to the console. ;
;------------------------------------------------------------------------------;
ANSI_INT_29 PROC FAR
STI
PUSH AX ;Save all registers.
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH DS
PUSH ES
PUSH BP
CLD ;All string operations forward.
MOV BX,CS ;Point to our data segment.
MOV DS,BX
MOV ES,BX
CALL ANSI_STATE ;Call the current state of
; the ANSI Esc sequence.
POP BP
POP ES
POP DS
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX ;Restore all registers and
IRET ; and return
ANSI_INT_29 ENDP
;------------------------------------------------;
ANSI_INT_21 PROC FAR
PUSHF
CMP AH,0BH ;If DOS function 0Bh (Check
JNZ CK_INPUT ; Standard Input Status)
CMP CS:REASSIGN_FLAG,ON ; and reassignment in progress,
JNZ CK_INPUT ; including a Device Status
CALL DWORD PTR CS:OLD_INT_21 ; Request, then process call in
MOV AL,0FFH ; case control break pressed;
IRET ; then return 0FFh for char ready
CK_INPUT: CMP AH,3FH ;Read from STDIN?
JNZ CK_CONSOLE
OR BX,BX
JZ INPUT
CK_CONSOLE: CMP AH,0AH ;If DOS Int 21 functions Ah,
JZ INPUT ; 7h, 1h or console input (6h)
CMP AH,7 ; then tell INT 16, DOS input
JZ INPUT ; is active.
CMP AH,1
JZ INPUT
CMP AH,8
JZ INPUT
CMP AH,6
JNZ QUICK21_EXIT
CMP DL,0FFH
JZ INPUT
QUICK21_EXIT: POPF ;If not, let the original
JMP DWORD PTR CS:OLD_INT_21 ; interrupt process the call.
INPUT: POPF
MOV CS:DOS_INPUT,ON ;INT 16 handler flag.
PUSHF
CALL DWORD PTR CS:OLD_INT_21 ;Emulate an interrupt.
MOV CS:DOS_INPUT,OFF ;Turn flag back off.
STI
RET 2 ;Return with current flags.
ANSI_INT_21 ENDP
;-----------------------------------------------------------------;
; If we get here via a DOS console input call, any awaiting key ;
; in keyboard buffer is checked to see if it is to be reassigned. :
;-----------------------------------------------------------------;
ANSI_INT_16 PROC FAR
STI ;Interrupts back on.
PUSHF ;Preserve flags on stack.
PUSH BP
PUSH DS
PUSH AX
PUSH CS ;Point to our data.
POP DS
CMP DOS_INPUT,OFF ;If not called via a DOS
JZ QUICK16_EXIT ; console input call, exit.
CMP BUSY_FLAG,ON ;If already processing a call,
JZ QUICK16_EXIT ; exit.
CLD ;All string operations forward.
MOV BP,SP ;Base reference to flags on stack
MOV FUNCTION_16,AH ;Save function request.
AND AH,NOT 10H ;Strip possible extended request.
JZ GET_KEY ;If zero, it's wait for a key.
DEC AH ;Else, decrement. If zero it's
JZ KEY_STATUS ; key status, else exit.
QUICK16_EXIT: POP AX ;Restore registers and let
POP DS ; original interrupt handler
POP BP ; process the call.
POPF
JMP DWORD PTR CS:OLD_INT_16
KEY_STATUS: OR BYTE PTR [BP+6],ZR ;Assume none ready; ZR = 1.
GET_KEY: MOV BUSY_FLAG,ON ;Non-reentrant; flag busy.
POP AX
PUSH BX ;Save some more registers.
PUSH CX
PUSH DX
PUSH SI
PUSH DI
CMP REASSIGN_FLAG,ON ;Already in process of reassign?
JZ CK_BUFFER ;If yes, check kbd buffer.
PUSHF ;Else, emulate an interrupt.
CALL DWORD PTR OLD_INT_16
PUSHF ;Status call results in flags.
TEST FUNCTION_16,1 ;Was it a status call?
JZ CK_DUP ;If no, check key returned.
POPF ;Else, retrieve status results.
JZ INT16_EXIT ;If zero, no key waiting.
AND BYTE PTR [BP+6],NOT ZR ;Else, ZR = 0.
PUSHF ;PUSHF just to keep stack right.
CK_DUP: POPF ;Fix stack.
TEST STATUS,KOFF ;:If KOFF then we don't do
JNZ INT16_EXIT ;:re-assignments
MOV BX,AX ;Match procedure wants key in BX.
CALL CK_MATCH ;Check reassignment buffer.
JC INT16_EXIT ;If no match, exit.
MOV AX,[DI] ;Else, retrieve string length.
SUB AX,3 ;Adjust for length word and match
ADD DI,3 ; byte; same for string pointer.
OR BL,BL ;Extended key? ie. key = 0.
JNZ STORE_COUNT ;If no, save length and pointer.
DEC AX ;Else, adjust for extended code.
INC DI
STORE_COUNT: MOV REASSIGN_COUNT,AX ;Save string length.
MOV REASSIGN_POS,DI ;Save pointer to string.
MOV REASSIGN_FLAG,ON ;Flag that replacement in effect.
CK_BUFFER: TEST FUNCTION_16,1 ;Was it a key wait function call?
JNZ RETRIEVE ;If no, key status; get it.
CMP REMOVE_FLAG,OFF ;Removed it from kbd buffer?
JZ RETRIEVE ;If yes, get reassignment.
MOV AH,FUNCTION_16 ;Else, eat the replaced character
INT 16H ; via INT 16.
MOV REMOVE_FLAG,OFF ;Flag character eaten.
RETRIEVE: MOV DI,REASSIGN_POS ;Retrieve pointer to string.
MOV AX,[DI] ;Retrieve replacement character.
TEST FUNCTION_16,1 ;Is it a key wait function call?
JZ REMOVE ;If yes, bump pointer up one.
AND BYTE PTR [BP+6],NOT ZR ;If no, status call; ZR = 0
JMP SHORT INT16_EXIT ;All done.
REMOVE: INC REASSIGN_POS ;Move pointer to next character.
DEC REASSIGN_COUNT ;One less character to replace.
JZ REASSIGN_DONE ;If end of string, reset flags.
OR AL,AL ;Else, extended replacement?
JNZ INT16_EXIT ;If no, done here.
INC REASSIGN_POS ;Else adjust pointers.
DEC REASSIGN_COUNT
JNZ INT16_EXIT ;End of string?
REASSIGN_DONE: MOV REASSIGN_FLAG,OFF ;If yes, reset flags.
MOV REMOVE_FLAG,ON
INT16_EXIT: MOV BUSY_FLAG,OFF ;Reset the busy flag.
POP DI ;Restore registers.
POP SI
POP DX
POP CX
POP BX
POP DS
POP BP
POPF ;Flags on stack have status
RET 2 ; results; kill old flags.
ANSI_INT_16 ENDP
if PCB ;$
ANSI_INT_08 PROC FAR ;@
CMP CS:[PAUSE_TIMER],0 ;@
JE SKIP_TIMER ;@
DEC CS:[PAUSE_TIMER] ;@
SKIP_TIMER: JMP DWORD PTR CS:OLD_INT_08 ;@
ANSI_INT_08 ENDP ;@
endif ;$
;^============================================================================
;^ MUXINT processes calls to interrupt 2Fh
;^ Entry: AH - Device ID
;^ Exit: AL - 0FFh if AH = Alias device ID. Unchanged otherwise.
;^ ES - Code segment if AH = Alias device ID. Unchanged otherwise.
;^============================================================================
muxint proc far ;^
cmp ah,cs:[multiplex_id] ;^ Check for program ID
je muxint_1 ;^ Its us, indicate installed.
jmp cs:[int2fh] ;^ else pass the call on
muxint_1: ;^
mov al,-1 ;^ Indicate Alias installed
push cs ;^ ES = installed code segment
pop es ;^
iret ;^
muxint endp ;^
;************* ANSI ESCAPE STATE ROUTINES ************* ;
ESC_STATE: MOV BX,OFFSET BRACKET_STATE ;Assume Esc character.
CMP AL,ESC_CHAR ;Is it Esc? If yes, store
JZ SHORT_JUMP ; char. and next state.
if PCB ;$
CMP DOS_INPUT,ON ;@ Called via DOS Input?
JE WRITE_CHAR2 ;@ Yes, Nevermind
MOV BX,OFFSET ATX_STATE ;@ No, Assume @ Character
CMP AL,'@' ;@ Is it @? If yes, store
JZ SHORT_JUMP ;@ char. and next state.
WRITE_CHAR2:
endif ;$
JMP WRITE_CHAR ;@ Else, normal char; write it.
;------------------------------------------------;
BRACKET_STATE: MOV BX,OFFSET SPECIAL_STATE ;Assume left bracket.
CMP AL,'[' ;Is it left bracket? If yes,
SHORT_JUMP: JZ STORE_STATE ;store char. and next state.
JMP FLUSH_BUFFER ;Else, flush Esc out of buffer.
;---------------------------------------------------------------;
; Must process <ESC>[2J (CLS) regardless if ANSI.COM ON or OFF. ;
;---------------------------------------------------------------;
SPECIAL_STATE: MOV BX,OFFSET CLS_STATE ;Assume Erase in Display code.
CMP AL,'2' ;Is it the '2' ?
JZ STORE_STATE ;If yes, progress to next state.
TEST STATUS,OFF ;Else, is ANSI OFF ?
JNZ FLUSH_BUFFER ;If yes, flush Esc, bracket.
MOV BX,OFFSET PARAM_STATE ;Else, check for first Set,
CMP AL,'=' ; Reset Mode characters of
JZ STORE_STATE ; '=' and '?'.
CMP AL,'?'
JZ STORE_STATE ;If found, store.
JMP SHORT DO_PARAMETER ;Else, check other codes.
;------------------------------------------------;
CLS_STATE: CMP AL,'J' ;Is it second character of CLS?
MOV DI,OFFSET COMMAND_END ;If yes, execute
JZ EXECUTE ; Erase in Display procedure.
TEST STATUS,OFF ;ANSI OFF? If yes, flush buffer.
JNZ FLUSH_BUFFER ; If not fall through to process.
MOV BYTE PTR NUMBER_BUFFER,2 ;Store the previous 2.
DO_PARAMETER: MOV ANSI_STATE,OFFSET PARAM_STATE ;Parameter state.
;------------------------------------------------;
PARAM_STATE: CALL CK_QUOTE ;Is it single or double quotes?
JZ STORE_STATE ;If yes, store string state.
CMP AL,';' ;Is it semi-colon delimiter?
JNZ CK_NUMBER ;If no, check if number.
INC NUMBER_COUNT ;Else, increment number count.
JMP SHORT BUFFER_CHAR ;Buffer the semi-colon.
CK_NUMBER: CMP AL,'0' ;Is it below 0 ?
JB FLUSH_BUFFER ;If yes, illegal; flush buffer.
CMP AL,'9' ;Else, is it above 9 ?
JA DO_COMMAND ;If yes, check for command char.
CALL ACCUMULATE ;Else it's a number; accumulate.
JMP SHORT BUFFER_CHAR ;Buffer the character.
DO_COMMAND: MOV DI,OFFSET COMMAND_TABLE ;Point to legal ANSI commands.
MOV CX,COMMAND_LENGTH ;Number of commands.
REPNZ SCASB ;Check for a match.
JNZ FLUSH_BUFFER ;If no match, flush sequence.
INC NUMBER_COUNT ;Else, increment for last number.
MOV DI,OFFSET COMMAND_END ;Point to appropriate command
SHL CX,1 ; processing procedure.
SUB DI,CX
EXECUTE: CALL GET_BIOS_DATA ;Get cursor position data.
CALL DS:[DI] ;Do command subroutine.
JMP SHORT FLUSH_END ;Clear buffer.
;------------------------------------------------;
QUOTE_STATE: CMP AL,QUOTE_TYPE ;Is it an ending string quote?
JNZ BUFFER_CHAR ;If no, buffer literal.
MOV BX,OFFSET PARAM_STATE ;Else, back to parameter parsing.
;------------------------------------------------;
STORE_STATE: MOV ANSI_STATE,BX ;Store the ANSI state.
;------------------------------------------------;
BUFFER_CHAR: MOV DI,ESC_COUNT ;Buffer the character in case
CMP DI,ESC_BUFFER_SIZE ; ending ANSI command is illegal.
JZ FLUSH_BUFFER ;If buffer full, flush.
ADD DI,OFFSET ESC_BUFFER ;Point to next buffer position.
STOSB ;Store the character.
INC ESC_COUNT ;Increment the sequence count.
RET
;------------------------------------------------;
FLUSH_BUFFER: ;$
if PCB ;$
CALL BUFFER_CHAR ;@ Buffer current character also!
FLUSH_BUFF2: ;@
else ;$
PUSH AX ;Save the current character. Might need removal
endif ;$
MOV SI,OFFSET ESC_BUFFER ;Point to the sequence buffer.
MOV CX,ESC_COUNT ;Count of buffered characters.
if pcb ;~
FLUSH_BUFF3: PUSH CX ;~
STC ;~Indicate LEFT PADDING
CALL PAD_STRING ;~
endif ;~
NEXT_FLUSH: LODSB ;Retrieve one.
PUSH CX ;Save counter and pointer.
PUSH SI
CALL WRITE_CHAR ;Write character to screen.
POP SI ;Restore counter and pointer.
POP CX
LOOP NEXT_FLUSH ;Flush entire buffer.
ife PCB ;$
POP AX ;@Retrieve last character. Might need removal
CALL WRITE_CHAR ;@Write it also. Might need removal
endif ;$
if PCB ;~
POP CX ;~
CLC ;~Indicate RIGHT PADDING
CALL PAD_STRING ;~
endif ;~
FLUSH_END: MOV ESC_COUNT,0 ;Reset counter.
MOV ANSI_STATE,OFFSET ESC_STATE ;Back to Esc state.
MOV NUMBER_COUNT,0 ;Reset parameter counter.
PUSH CS ;Point to our data.
POP ES
MOV DI,OFFSET NUMBER_BUFFER ;Clear number buffer
MOV CX,ESC_BUFFER_SIZE / 2 ; to zeros.
XOR AX,AX
REP STOSW
RET
if PCB ;$
;------------------------------------------------;@
COLOR_STATE: MOV AH,AL ;^ Save character
CMP AL,'0' ;@ Is it below 0 ?
JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
CMP AL,'9' ;@ Else, is it 0 - 9 ?
JBE DO_COLOR ;@ If yes, Make color.
CMP AL,'A' ;@ Is it below A ?
JB FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
CMP AL,'F' ;@ Is it Above F ?
JA FLUSH_BUFFER ;@ If yes, illegal; flush buffer.
SUB AL,7 ;@ Else, adjust for Hex
DO_COLOR: CALL ACCUM_COLOR ;@ Accumulate Color.
MOV AH,AL ;^ get saved character
INC NUMBER_COUNT ;@ Count the character
CMP NUMBER_COUNT,2 ;@ Already got two?
JNE SHORT BUFFER_CHAR ;~;&;@ No, Buffer the character.
TEST STATUS,OFF ;@ Else, is ANSI OFF ?
JNZ FLUSH_BUFFER ;@ If yes, flush Esc, bracket.
TEST STATUS,XOFF ;^ Are @Xnn colors off?
JNZ FLUSH_END ;^ Yes, Ignore this completely
INC CL ;& Is it @XFF restore code?
JNZ COLOR_2 ;& No, Check for save code
MOV CL,SAVE_ATTRIBUTE ;& Yes, Get back saved attribute
JMP SHORT COLOR_3 ;& and set it
COLOR_2: DEC CL ;& Is it @X00 save code
JNZ COLOR_3 ;& No, just set it then
MOV CL,ATTRIBUTE ;& Yes, Save the attribute
MOV SAVE_ATTRIBUTE,CL ;&
JMP FLUSH_END ;& and flush it
COLOR_3: MOV ATTRIBUTE,CL ;@ Set Attribute
JMP FLUSH_END ;@ Clear Buffer
;------------------------------------------------;~
POSITION_SUB: CMP AL,'@' ;~ Is it another @
JE FOUND_END ;~ Yes, so process it
CMP AL,'0' ;~ No, is it Numeric?
JB SHORT_FLUSH2 ;~ No, and invalid!
CMP AL,'9' ;~ Maybe, let's see
JA CHECK_TYPE ;~ No, maybe a justification
CALL ACCUMULATE ;~ Build the number
JMP SHORT BUFFER_IGNORE ;~
CHECK_TYPE: MOV AH,AL ;~ Copy it so we can Capitalize
AND AH,0DFh ;~ Force Upper Case
CMP AH,'C' ;~ Centered?
JE POS_TYPE ;~ Yes,
CMP AH,'R' ;~ No, Right Justified?
JE POS_TYPE ;~ Yes,
SHORT_FLUSH2: MOV IGNORE,0 ;~ No, So Zero out IGNORE!
JMP SHORT_FLUSH ;~ Invalid character
POS_TYPE: MOV JUSTIFY, AH ;~ Yes, Save the type
BUFFER_IGNORE: INC IGNORE ;~ We have to ignore these
BUFFER_JUMP: JMP BUFFER_CHAR ;~ But buffer the character also
;------------------------------------------------;@
VARIABLE_SUB: CMP AL,':' ;~ Is it a ':' instead of '@'
JNE VARIABLE_2 ;~ No, continue
MOV BYTE PTR NUMBER_BUFFER,0;~ Reset Variable
MOV JUSTIFY,'L' ;~ Reset justification to default
MOV BX, OFFSET POSITION_SUB ;~ Yes, Redirect the ANSI_STATE
INC IGNORE ;~ We need to ignore this
JMP STORE_STATE ;~ Store the state
VARIABLE_2: ;~
CMP AL,'@' ;@ Is it another @
JE FOUND_END ;~ Yes, so process it
JB SHORT_FLUSH2 ;@ No, and not valid, so flush
CMP AL,'Z' ;~ Is it UpperCase
JA SHORT_FLUSH2 ;~ No, so flush
JMP BUFFER_JUMP ;&;@ No, Buffer the Char (indirect)
FOUND_END: ;~
MOV DX,OFFSET AT_VARIABLE_TABLE ;@ Point to Variable List
XOR BX,BX ;@ Position in Variable List
NEXT_VARI: MOV DI,DX ;@ Load DI
INC BX ;@ Count the Variable
MOV SI,OFFSET ESC_BUFFER ;@ Point to our Variable
MOV CX,OFFSET LAST_VARIABLE ;@ Point to End of list
SUB CX,DI ;@ Minus our current location
MOV AL,'@' ;@ We need to start on these
REPNE SCASB ;@ so find one
JCXZ SHORT_FLUSH ;@ End of List? Yes
MOV DX,DI ;@ No,Save Variable list pointer
DEC DI ;@ No, Back up to the @
MOV CX,ESC_COUNT ;@ Length of Variable
SUB CX,IGNORE ;~ But ignore the last few maybe
REP CMPSB ;@ See if this one matches?
JNZ NEXT_VARI ;@ Didn't match that one!
CMP [DI],AL ;@ Last Character has to be an @
JNZ NEXT_VARI ;@ Nope
DEC BX ;@ Back up 1
CMP BX,ACTION_CODES-1 ;@ Is it an Action code ;~
JA NOT_ACTION ;@ No
SHL BX,1 ;@ BX x 2 for proper offset
MOV DI,OFFSET JUMP_TABLE ;@ Point to command table
ADD DI,BX ;@ Adjust to the desired command
JMP EXECUTE ;@ Execute the command
NOT_ACTION: MOV SI, OFFSET SUB_TABLE ;@ Point to Sub Table
MOV AX,BX ;@ Save Variable Number
MOV CX,BX ;@ Copy Variable Number
XOR BX,BX ;@ Zero Offset
XOR DX,DX ;@ Zero out initial length
SUB CX,ACTION_CODES-1 ;@ Adjust Variable Number ;~
NEXT_OFFSET: ADD BX,DX ;@ Add last Length to Offset
MOV DL,[SI] ;@ Get Length
INC SI ;@ Bump the pointer
LOOP NEXT_OFFSET ;@ Do all of them
MOV CX,DX ;@ Get last Length
MOV SI,OFFSET SUB_LIST ;@ Point to the substitue buffer.
ADD SI,BX ;@ Add Offset
SUB AX,STRING_CODES ;@ Is It a String Variable ;~
JB IS_STRING ;@ Yes, Flush our Substitute
CMP AX,1 ;@ Is it Date ot Time?
JBE IS_TIMEDATE ;@ No, It's a Number
SUB AX,2 ;@ Adjust Variable number
CALL NUMBER_PARSE ;@ Create a Numeric value then
JMP SHORT VAR_END ;@ Flush our Number
IS_TIMEDATE: CALL MAKE_TIMEDATE ;@
IS_STRING: MOV DI,SI ;@ Copy Pointer to DI for SCASB
MOV AL,0 ;@ We look for a Zero
REPNE SCASB ;@ Findit it
JNE GOT_END ;@ Skip if we didn't hit a Zero
DEC DI ;@ Back up if we did
GOT_END: MOV CX,DI ;@ Get Current Location
SUB CX,SI ;@ Subtract Starting Location
VAR_END: JMP FLUSH_BUFF3 ;~ ;@ Flush our Substitute
SHORT_FLUSH: PUSH AX ;& Save Character
CALL FLUSH_BUFF2 ;@ Flush what we have now
POP AX ;& Restore Character
JMP ESC_STATE ;& And save the new state
;------------------------------------------------;~
PAD_STRING: PUSH CX ;~ Save count
PUSH SI ;~ Save Pointer
LAHF ;~ Save Carry in AH
CMP IGNORE,0 ;~ Do we need to pad?
JE NO_PAD ;~ No, nevermind
MOV BL,BYTE PTR NUMBER_BUFFER ;~
XOR BH,BH ;~ Get the length desired in BX
CMP BX,CX ;~ Variable Longer?
JL NO_PAD ;~ Yes, nevermind
SUB BX,CX ;~ We need this many spaces
XOR BH,BH ;~ Keep it short
MOV CX,BX ;~ We need it in CX
CMP JUSTIFY,'R' ;~ Right justification?
JNE PAD_2 ;~ No, Left or Center
SAHF ;~ Yes, Get Carry NC=RIGHT PAD
JNC NO_PAD ;~ Nevermind, Were not doing it
JMP SHORT DO_PAD ;~ Pad it
PAD_2: CMP JUSTIFY,'C' ;~ Center justification?
JE PAD_3 ;~ Yes,
SAHF ;~ No, Must be Left (Default)
JC NO_PAD ;~ Nevermind, Were not doing it
JMP SHORT DO_PAD ;~ Pad it
PAD_3: SHR CX,1 ;~ Centering Divide by 2
SAHF ;~ Yes, Get Carry NC=RIGHT PAD
JNC DO_PAD ;~ OK, we are padding the RIGHT
SUB BX,CX ;~ OK, we are padding the LEFT
MOV CX,BX ;~ so use the remainder
DO_PAD: CALL POS_IT0 ;~ So go write the spaces.
NO_PAD: POP SI ;~ restore pointer
POP CX ;~ restore count
RET ;~ and back we go.
;------------------------------------------------;@
ATX_STATE: MOV BX,OFFSET COLOR_STATE ;@ Assume 'X' Character
CMP AL,'X' ;@ Is it X? If yes, store
JZ SHORT_STORE ;@ char. and next state.
CMP AL,'@' ;@ Is it another '@' already?
JZ WRITE_CHAR ;@ Yes!
CMP AL,ESC_CHAR ;& Is it an Escape
JE SHORT_FLUSH ;& Yes, so flush buffer & restart
MOV BX,OFFSET VARIABLE_SUB ;@ No, Must be a Variable
MOV IGNORE,0 ;~ Reset IGNORE Count to Zero
SHORT_STORE: JMP STORE_STATE ;@ Store the state
endif ;$
;------------------------------------------------;
; Slow video writes are via BIOS Write TTY. ;
;------------------------------------------------;
WRITE_CHAR: CMP AL,BELL ;Let BIOS process BS and BELL.
JZ WRITE_TTY
CMP AL,BS
JZ WRITE_TTY
CALL GET_BIOS_DATA ;Get BIOS video data.
CMP AL,TAB ;Is character a TAB?
JNZ CK_ACTIVE ;If no, process normally.
MOV CX,CURSOR_POSN ;Else, expand TAB to
AND CX,7 ; appropriate space characters.
NEG CX
ADD CX,8
NEXT_TAB: PUSH CX
MOV AL,SPACE
CALL CK_ACTIVE
POP CX
LOOP NEXT_TAB
RET
CK_ACTIVE: TEST STATUS,OFF ;Is ANSI OFF?
JNZ WRITE_TTY ;If yes, write via BIOS TTY.
CK_FAST: CALL CK_SLOW_TEXT ;Is ANSI SLOW or in graphics
JNC WRITE_FAST ; mode? If no, write fast.
CMP AL,CR ;Let BIOS handle CR and LF.
JZ WRITE_TTY
CMP AL,LF
JZ WRITE_TTY
WRITE_SLOW: PUSH AX ;Else, write character/attribute
MOV CX,1 ; at current cursor position
MOV BH,ACTIVE_PAGE ; via BIOS.
MOV BL,ATTRIBUTE
MOV AH,9
INT 10H
POP AX
CMP LINE_WRAP,ON ;Is line wrap on?
JZ TTY ;If yes, continue.
MOV CX,CRT_COLS ;Else, cursor at rightmost
DEC CL ; column?
CMP CL,CURSOR_COL ;If yes, continue, else
JNZ TTY ; return without writing.
RET
;------------------------------------------------;
WRITE_TTY: MOV BL,7 ;Attribute in graphics mode.
TTY: MOV AH,0EH
INT 10H
RET
;----------------------------------------------------------------------------;
; Fast screen writes are directly to the video buffer without retrace check. ;
;----------------------------------------------------------------------------;
WRITE_FAST: PUSH ES ;Preserve extra segment.
MOV DX,CURSOR_POSN ;Retrieve cursor position.
CMP AL,CR ;Carriage return?
JNZ CK_LINEFEED ;If no, check linefeed.
XOR DL,DL ;Else, cursor to first column.
JMP SHORT UPDATE_CURSOR
CK_LINEFEED: CALL VIDEO_SETUP ;Calculate video address.
CMP AL,LF ;Linefeed?
JZ NEXT_ROW ;If yes, next row.
MOV AH,ATTRIBUTE ;Retrieve attribute.
STOSW ;Put char/attrib in video buffer.
INC DL ;Increment cursor column.
CMP DL,BYTE PTR CRT_COLS ;End of row?
JB UPDATE_CURSOR ;If no, update cursor.
CMP LINE_WRAP,OFF ;Else, line wrap off?
JZ FAST_END ;If yes, don't move cursor.
XOR DL,DL ;Else, column zero.
NEXT_ROW: INC DH ;Next row.
CALL INFORMATION ;Get displayable row info.
CMP DH,AL ;Beyond the bottom of screen?
JBE UPDATE_CURSOR ;If no, update cursor.
DEC DH ;Else, cursor to original row.
MOV AX,CRT_COLS ;. Get number of columns
PUSH DX ;. Save Cursor position
PUSH AX ;. Save Columns
SHL AX,1 ;. Twice for Char/Attribute
MOV SI,AX ;. Starting offset in SI
MOV DL,START_SCROLL ;. Where we allow scroll to start
MUL DL ;. Calculate offset in AX
SUB DH,DL ;. Reduce line count
MOV DI,CRT_START ;Point destination to top.
ADD DI,AX ;. Add offset
ADD SI,DI ;. Point source to second row
POP AX ;. Get Back Columns
PUSH AX ;. Save it again
MUL DH ;Times displayable rows - 1.
MOV CX,AX ; equals char/attrib to scroll.
PUSH DS ;Save data segment and
PUSH ES ; point to video segment.
POP DS
REP MOVSW ;Scroll the screen.
POP DS ;Restore data segment.
POP CX ;Retrieve CRT columns.
POP DX ;.Get back Cursor info
MOV AL,SPACE ;Write space/attrib to
MOV AH,ATTRIBUTE ; bottom row.
REP STOSW
UPDATE_CURSOR: CALL SET_CURSOR ;Update the cursor position.
FAST_END: POP ES ;Restore extra segment.
RET
if PCB ;$
;@************ SUPPORT ROUTINES *************;
QOFF: MOV AL,0FFH ;& Show QOFF
JMP SHORT QON2 ;&
QON: XOR AL,AL ;& Show QON
QON2: MOV Q_STATE,AL ;& Store the state
RET ;& All done
BEEP: MOV AL,7 ;@
JMP WRITE_CHAR ;@
MORE: MOV AX,MORE_DELAY ;@
JMP SHORT PAUSE2 ;@
PAUSE: MOV AX,PAUSE_DELAY ;@
PAUSE2: CMP Q_STATE,0 ;& Are we at a QOFF state
JNZ WAIT1 ;& Yes, then these are WAITs
MOV SI,OFFSET MORE_PROMPT ;@ Point to More? Prompt
MOV CX,MORE_LENGTH ;@ Get the Length of it
JMP SHORT WAIT2 ;@ Go Wait
WAIT1: MOV AX,WAIT_DELAY ;@
MOV SI,OFFSET WAIT_PROMPT ;@ Point to Wait Prompt
MOV CX,WAIT_LENGTH ;@ Get the Length of it
WAIT2: MOV PAUSE_TIMER,AX ;@
; PUSH CX ;@ Save Length
WAIT2_LOOP: LODSB ;@ Get Character
PUSH CX ;@
PUSH SI ;@
CALL WRITE_CHAR ;@ Output it
POP SI ;@
POP CX ;@
LOOP WAIT2_LOOP ;@ Do all the Characters
WAIT_LOOP: MOV AH,1 ;@ KeyBoard Status
INT 16H ;@ Keyboard I/O Services
JNZ CONTINUE ;@ Key Hit!
CMP PAUSE_TIMER,0 ;@ Timer Run out?
JNE WAIT_LOOP ;@ No
JMP SHORT WAIT3 ;@ Yes
CONTINUE: MOV AH,0 ;@ KeyBoard Read
INT 16H ;@ Keyboard I/O Services
WAIT3: ;@
XOR CL,CL ;@ Clear the Attribute
XCHG CL,ATTRIBUTE ;& and
PUSH CX ;& save it too
MOV DX,CURSOR_POSN ;& Get Cursor
MOV DL,0 ;& First column
CALL SET_CURSOR ;& Set the new Cursor
CALL ERASE_2 ;& Erase the line
;&
;& The following lines (and the PUSH CX above) will clear only the PROMPT
;& and leave the cursor where it was when the prompt appears. This was
;& different than PCBoard 14.5 so I changed it to clear the entire line
;& via the above four lines. Left the old code in case someome wanted it.
;&
;& POP CX ;@ Get back Length
;&ERASE_LOOP: PUSH CX ;@ Save it again
;& MOV AL,BS ;@ Send BS
;& CALL WRITE_CHAR ;@ character
;& MOV AL,' ' ;@ and then space
;& CALL WRITE_CHAR ;@ character to erase
;& MOV AL,BS ;@ then BS again
;& CALL WRITE_CHAR ;@ to backup.
;& POP CX ;@ get back length again
;& LOOP ERASE_LOOP ;@ Erase entire prompt.
POP CX ;& Restore
MOV ATTRIBUTE,CL ;& the Attribute
RET ;@
AUTOOFF: MOV AX,WAIT_DELAY ;@ Restore More to a Wait
JMP SHORT AUTO2 ;@
AUTOMORE: MOV AX,PAUSE_DELAY ;@ Make More a Pause
AUTO2: MOV MORE_DELAY,AX ;@
SKIP_IT: RET ;@
DELAY_IT: PUSH DX ;~ Save registers
PUSH BX ;~
MOV AX,182 ;~ Multiply number by 182
MOV BX,10 ;~ Then divide by 10
XOR DX,DX ;~ Prepare for division
MUL BYTE PTR NUMBER_BUFFER ;~
DIV BX ;~
MOV PAUSE_TIMER,AX ;~ This should be Num * 18.2
POP BX ;~ Restore registers
POP DX ;~
JMP WAIT_LOOP ;~ Now do the delay
POS_IT: MOV AL,BYTE PTR NUMBER_BUFFER ;~
MOV DX,CURSOR_POSN ;~ Get Cursor
INC DL ;~ Adjust for ordinal
SUB AL,DL ;~ Are we beyond it already?
JLE POS_IT2 ;~ Yes, nevermind
MOV CL,AL ;~ No, Put loop count in CX
XOR CH,CH ;~
POS_IT0: MOV AL,' ' ;~ Write Spaces
POS_IT1: PUSH CX ;~ Save the count
CALL WRITE_CHAR ;~
POP CX ;~ Restore the count
LOOP POS_IT1 ;~ Until Done
POS_IT2: RET ;~
endif ;$
;************* SUPPORT ROUTINES *************;
CK_QUOTE: MOV BX,OFFSET QUOTE_STATE ;Assume quote state.
MOV AH,DOUBLE_QUOTE
CMP AL,AH ;Is it double quote?
JZ GOT_QUOTE ;If yes, string delimiter.
MOV AH,SINGLE_QUOTE ;Is it single quote?
CMP AL,AH ;Is yes, string delimiter.
JNZ QUOTE_END ;Else, return ZR = 0.
GOT_QUOTE: MOV QUOTE_TYPE,AH ;Store as matching string end.
QUOTE_END: RET
;------------------------------------------------;
ACCUMULATE: PUSH AX ;Preserve number character.
SUB AL,'0' ;Convert ASCII to binary.
MOV CL,AL ;Save the number.
MOV AX,10 ;Multiply previous count by 10.
MOV BX,NUMBER_COUNT
MUL BYTE PTR NUMBER_BUFFER[BX]
ADD AL,CL ;Add in new number
MOV BYTE PTR NUMBER_BUFFER[BX],AL ; and store.
POP AX
RET
if PCB ;$
;------------------------------------------------;@
ACCUM_COLOR: PUSH AX ;@ Preserve number character.
SUB AL,'0' ;@ Convert ASCII to hex
MOV AH,BYTE PTR NUMBER_BUFFER ;@ Get current number
SHL AH,1 ;@ Shift left 4 bits
SHL AH,1 ;@
SHL AH,1 ;@
SHL AH,1 ;@
OR AH,AL ;@ Add in new number
MOV BYTE PTR NUMBER_BUFFER,AH ;@ and store.
MOV CL,AH ;@ Return with Color in CL
POP AX ;@
RET ;@
NUMBER_PARSE: MOV BX,[SI] ;@ Get low word
MOV DX,AX ;@ Save Variable Number
XOR AX,AX ;@ Assume Integer
CMP CX,4 ;@ Is that a Long Integer?
JNE SHORT_INT ;@ No, Integer
MOV AX,[SI + 2] ;@ Yes, Get high word
SHORT_INT: MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space
MOV CX,10 ;@ Set divisor to 10
XOR SI,SI ;@ Clear SI as counter
MOV COMMA,2 ;@ Assume a Ratio
CMP DX,2 ;@ Is it a Ratio?
JB GETDIGIT ;@ No
RESET_COMMA: MOV COMMA,4 ;@ Set Comma Counter
GETDIGIT: DEC DI ;@ Point DI at correct character
INC SI ;@ Register that we have a character
DEC COMMA ;@ Do we need a Comma?
JNZ NO_COMMA ;@ No
MOV BYTE PTR [DI],',' ;@ Yes, So Put one in!
JMP RESET_COMMA ;@ And restart the Comma count.
NO_COMMA: XOR DX,DX ;@ Clear DX to take remainder
DIV CX ;@ Divide AX first (High word)
MOV BP,AX ;@ Save quotient
MOV AX,BX ;@ Get low word
DIV CX ;@ DX had leftover from first divide
MOV BX,AX ;@ Save quotient
MOV AX,BP ;@ Put high word back
ADD DL,30h ;@ Make it an ASCII digit from remainder
MOV [DI],DL ;@ Put it in our string
OR AX,AX ;@ Is high word zero?
JNZ GETDIGIT ;@ No keep going
OR BX,BX ;@ Is low word zero?
JNZ GETDIGIT ;@ No keep going
MOV CX,SI ;@ Digit count to CX
MOV SI,DI ;@ String Pointer to SI
MOV DI, OFFSET PARSE_BUFFER ;@ Point DI at number space
CMP BYTE PTR [DI][-2],',' ;@ Was it a Ratio
JNE NOT_RATIO ;@ No, nevermind
MOV BYTE PTR [DI][-2],'.' ;@ Change comma to Decimal
MOV BYTE PTR [DI],':' ;@ Yes, Add ':1' to it
MOV BYTE PTR [DI][1],'1' ;@
ADD CX,2 ;@ Account for two more characers
NOT_RATIO: RET ;@
MAKE_TIMEDATE: MOV DI, OFFSET NUMBER_BUFFER ;@ Point to Number Buffer
MOV SI,DI ;@ Save it in SI too
JE MAKE_TIME ;@ It was TIME on Entry!
MOV CX,8 ;@ It was DATE! Length = 8
PUSH CX ;@ Save Length
MOV AH,04H ;@ Get date service
INT 1AH ;@ Call BIOS - return codes as follows:
PUSH CX ;@ CH = Century (19-20) CL = Year (00-99)
PUSH DX ;@ DH = Month (1-12) DL = Day (00-31)
MOV AL,DH ;@ Month
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,'-' ;@
STOSB ;@
POP AX ;@ Day - was DX when pushed
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,'-' ;@
STOSB ;@
POP AX ;@ Year - was CX when pushed
POP CX ;@ Restore Length
JMP SHORT ASCII ;@ Convert byte to ASCII digit
MAKE_TIME: MOV CX,5 ;@ Length = 5
PUSH CX ;@ Save Length
MOV AH,02H ;@ Get time service
INT 1AH ;@ Call BIOS - return codes as follows:
PUSH CX ;@ CH = Hours (0-23) CL = Minutes (0-59)
MOV AL,CH ;@ Hours
CALL ASCII ;@ Convert byte to ASCII digits
MOV AL,':' ;@
STOSB ;@
POP AX ;@ Minutes - was CX when pushed
POP CX ;@ Restore Length
ASCII: MOV AH,AL ;@ Need to get BCD Digits
SHR AL,1 ;@ MSD in AL
SHR AL,1 ;@ lower
SHR AL,1 ;@ 4
SHR AL,1 ;@ Bits
AND AH,0FH ;@ And LSD in AH cleanly
OR AX,3030H ;@ Convert to ASCII
STOSW ;@ Stuff Digits into Buffer (in reverse)
RET ;@ SI Points to String Data
endif ;$
;---------------------------------------------------------------------------;
; OUTPUT: CY = 1 if write SLOW mode or in graphics mode; CY = 0 otherwise. ;
;---------------------------------------------------------------------------;
CK_SLOW_TEXT: TEST STATUS,SLOW
JNZ SLOW_MODE
CMP CRT_MODE,7
JZ TEXT_MODE
CMP CRT_MODE,3
JA SLOW_MODE
TEXT_MODE: CLC
RET
SLOW_MODE: STC
RET
;-------------------------------------;
; OUTPUT: AL = Screen rows minus one ;
;-------------------------------------;
INFORMATION: PUSH DS ;Save data segment.
MOV AX,40H ;Point to BIOS data.
MOV DS,AX
MOV AL,DS:[84H] ;Retrieve rows - 1.
OR AL,AL ;BIOS supported?
JNZ INFO_END ;If yes, done here.
MOV AL,24 ;Else, assume 25 lines.
INFO_END: POP DS
RET
;------------------------------------------------------------------------------;
; INPUT: DX = Cursor position. ;
; OUTPUT: ES = Video buffer segment; DI = Video buffer offset; AX,DX preserved ;
;------------------------------------------------------------------------------;
VIDEO_SETUP: PUSH AX
MOV AX,CRT_COLS ;Retrieve CRT columns.
MUL DH ;Times cursor row.
MOV BL,DL
XOR BH,BH
ADD AX,BX ;Plus cursor column.
SHL AX,1 ;Times two for attribute.
MOV DI,CRT_START ;Plus starting video offset.
ADD DI,AX ;Equals destination.
MOV BX,0B000H ;Assume mono card.
CMP ADDR_6845,3B4H ;Is it mono port?
JZ VIDEO_SEGMENT ;If yes, guessed right.
ADD BX,800H ;Else, point to color segment.
VIDEO_SEGMENT: MOV ES,BX
POP AX
RET
;------------------------------------------------;
; Move BIOS video data into our data segment. ;
;------------------------------------------------;
GET_BIOS_DATA: PUSH DS
PUSH DI ;Point to BIOS data segment.
MOV BX,40H
MOV DS,BX
MOV SI,BIOS_ACTIVE_PAGE ;Start with active page.
MOV DI,OFFSET ACTIVE_PAGE
MOVSB ;Retrieve active page
MOVSW ; and address of 6845 port.
MOV SI,BIOS_CRT_MODE ;Retrieve CRT mode, CRT columns,
MOV CX,CRT_DATA_LENGTH ; CRT length, CRT start.
REP MOVSB
MOV BL,ES:ACTIVE_PAGE ;Use active page as index
XOR BH,BH ; of active cursor position.
SHL BX,1
ADD SI,BX
MOVSW
POP DI
POP DS
RET
;----------------------------------------------------------------------------;
; OUTPUT: BL = First parameter; BH = Second parameter; DX = cursor position. ;
;----------------------------------------------------------------------------;
ADJUST_NUMBER: MOV BX,WORD PTR NUMBER_BUFFER
OR BH,BH
JNZ ADJUST_AL ;If either parameter zero,
INC BH ; increment to convert
ADJUST_AL: OR BL,BL ; to base one.
JNZ ADJUST_END
INC BL
ADJUST_END: MOV DX,CURSOR_POSN ;Return cursor position.
RET
;--------------------------------------------------------------------------;
; INPUT: AX = number; BP = 0 if console output; BP = 1 if storage output. ;
;--------------------------------------------------------------------------;
DECIMAL_OUT: MOV BX,10 ;Divisor of ten.
XOR CX,CX ;Zero in counter.
NEXT_COUNT: XOR DX,DX ;Zero in high half.
DIV BX ;Divide by ten.
ADD DL,'0' ;Convert to ASCII.
PUSH DX ;Save results.
INC CX ;Also increment count.
CMP AX,0 ;Are we done?
JNZ NEXT_COUNT ;Continue until zero.
OR BP,BP ;If BP zero, output to screen.
JNZ DEVICE_OUTPUT ;Else, store in buffer.
REPORT_OUTPUT: POP AX ;Retrieve number.
CALL PRINT_CHAR ;Display it.
LOOP REPORT_OUTPUT
RET
DEVICE_OUTPUT: CMP CX,2 ;Two digits?
JZ TWO_DIGITS ;If yes, process normally.
MOV AL,'0' ;Else, store leading zero.
STOSB
TWO_DIGITS: POP AX ;Retrieve number.
STOSB ;Store it
LOOP TWO_DIGITS
RET
;************* ANSI COMMAND SUBSET *************;
CLS: MOV BH,7 ;Assume normal attribute.
MOV BL,BYTE PTR CRT_MODE ;Get current video mode.
CMP BL,4
JBE CK_CLS_STATUS ;If text mode then check if
CMP BL,7 ; ANSI active.
JZ CK_CLS_STATUS
XOR BH,BH ;Else, use black attribute
TEST STATUS,OFF
JNZ CLS_SLOW ;If ANSI inactive, via BIOS.
JMP SHORT CK_CLS_SLOW ;Else, check if FAST or SLOW.
CK_CLS_STATUS: TEST STATUS,OFF ;Is ANSI OFF?
JNZ CLS_SLOW ;If yes, CLS via BIOS.
MOV BH,ATTRIBUTE ;Else, current attribute.
CK_CLS_SLOW: CALL CK_SLOW_TEXT
JC CLS_SLOW ;If ANSI SLOW, via BIOS.
CLS_FAST: MOV AH,BH ;Else, attribute in high half.
MOV AL,SPACE ;Space character in low half.
XOR DX,DX ;Cursor position home.
MOV DH,START_SCROLL ;$ protect lines
CALL VIDEO_SETUP ;Calculate video address.
MOV CX,CRT_LEN ;Retrieve CRT length.
SHR CX,1 ;Times two for attribute.
REP STOSW ;Write directly to video buffer.
JMP SHORT SET_CURSOR ;Set the cursor top left corner.
CLS_SLOW: CALL INFORMATION ;Get displayable rows.
MOV DH,AL ;Store in DH.
MOV DL,BYTE PTR CRT_COLS ;Retrieve CRT columns.
DEC DL ;Adjust to zero base.
XOR CX,CX ;Scroll active page.
MOV AX,600H
INT 10H
XOR DX,DX ;Home the cursor.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_POSITION: ;These two commands are the same.
HORZ_VERT_POS: CALL INFORMATION ;Returns AL = screen rows.
CALL ADJUST_NUMBER ;Returns BL,BH = parameters.
SUB BX,101H ;Zero based.
CMP BL,AL ;If cursor request within
;$ JA CURSOR_END ; boundaries of screen, set it.
JBE ROWOK ;$boundaries of screen, set it.
MOV BL,AL ;$Set to last row
ROWOK: CMP BH,BYTE PTR CRT_COLS ;$
;$ JAE CURSOR_END
JB COLOK ;$
MOV BH,BYTE PTR CRT_COLS ;$Set to
DEC BH ;$ last column
COLOK: XCHG BH,BL ;
MOV DX,BX
SET_CURSOR: MOV BH,ACTIVE_PAGE ;Set cursor via BIOS.
MOV AH,2
INT 10H
CURSOR_END: RET
;------------------------------------------------;
CURSOR_UP: CALL ADJUST_NUMBER ;Move cursor up one or more rows
OR DH,DH ; without changing column.
JZ CURSOR_END ;If already at top, ignore.
SUB DH,BL
JNC SET_CURSOR ;Stay in bounds.
XOR DH,DH
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURSOR_DOWN: CALL INFORMATION ;Returns AL = screen rows.
CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
CMP DH,AL ;Move cursor down requested
JZ CURSOR_END ; rows without changing column.
ADD DH,BL
CMP DH,AL
JNA SET_CURSOR
MOV DH,AL ;Stay in bounds.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_FORWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
MOV BH,BYTE PTR CRT_COLS ;Retrieve displayable columns.
DEC BH ;Adjust to zero base.
CMP DL,BH ;Move cursor forward one or more
JZ CURSOR_END ; columns without changing row.
ADD DL,BL
CMP DL,BH
JNA SET_CURSOR
MOV DL,BH ;Stay in bounds.
JMP SHORT SET_CURSOR
;------------------------------------------------;
CURS_BACKWARD: CALL ADJUST_NUMBER ;Returns BL,BH =nos.; DX = cursor
OR DL,DL ;Move cursor backward one or
JZ CURSOR_END ; more columns without changing
SUB DL,BL ; row. Ignore if already left.
JNC SET_CURSOR
XOR DL,DL ;Stay in bounds.
JMP SHORT SET_CURSOR
;--------------------------------------------------------;
; Report cursor position via console; Format: ESC[#;#R ;
;--------------------------------------------------------;
DEVICE_STATUS: MOV DI,OFFSET DEVICE_STATUS_BUFFER
MOV AL,ESC_CHAR
STOSB ;Store leading Esc, bracket
MOV AL,'[' ;in special Device Status Buffer.
STOSB
MOV AL,CURSOR_ROW ;Retrieve cursor row.
XOR AH,AH ;Zero in high half.
INC AX ;Convert to base one.
MOV BP,1 ;Flag as storage.
CALL DECIMAL_OUT ;Convert to ASCII.
MOV AL,';' ;Store delimiting semi-colon.
STOSB
MOV AL,CURSOR_COL ;Do same with cursor column.
XOR AH,AH
INC AX
CALL DECIMAL_OUT
MOV AL,'R' ;Add command character
STOSB
MOV AL,CR ; and carriage return.
STOSB
SUB DI,OFFSET DEVICE_STATUS_BUFFER ;Setup for console out.
MOV REASSIGN_COUNT,DI
MOV REASSIGN_POS,OFFSET DEVICE_STATUS_BUFFER
MOV REASSIGN_FLAG,ON
MOV REMOVE_FLAG,OFF
RET
;------------------------------------------------;
SAVE_CURSOR: MOV DX,CURSOR_POSN
MOV SAVE_POSITION,DX
RET
;------------------------------------------------;
RESTORE_CURS: MOV DX,SAVE_POSITION
JMP SET_CURSOR
;------------------------------------------------;
ERASE_IN_LINE: MOV DX,CURSOR_POSN ;Erase from the cursor to
ERASE_2: MOV CX,CRT_COLS ;& the end of the line, including
SUB CL,DL ; the current cursor position.
CALL CK_SLOW_TEXT
JC ERASE_SLOW ;If ANSI ON and not graphics,
CALL VIDEO_SETUP ; write directly to video buffer
MOV AL,SPACE ; with space/attribute.
MOV AH,ATTRIBUTE
REP STOSW
RET
ERASE_SLOW: MOV CX,DX ;Else, erase SLOW via
MOV DL,BYTE PTR CRT_COLS ; BIOS scroll active page.
DEC DL
MOV BH,ATTRIBUTE
MOV AX,601H
INT 10H
RET
;------------------------------------------------;
; Set Graphics Rendition ;
;------------------------------------------------;
SGR: MOV SI,OFFSET NUMBER_BUFFER ;Point to number parameters.
NEXT_SGR: LODSB ;Retrieve parameter.
MOV DI,OFFSET ATTRIBUTE_TABLE ;Look up attribute in
MOV CX,ATTRIBUTE_LENGTH ; translation table.
REPNZ SCASB
JNZ SGR_LOOP
MOV DI,OFFSET ATTRIBUTE_END
SHL CX,1
SUB DI,CX
MOV AX,[DI]
AND ATTRIBUTE,AL ;If match, AND with strip mask.
OR ATTRIBUTE,AH ;OR with add mask.
SGR_LOOP: DEC NUMBER_COUNT ;Do all parameters.
JNZ NEXT_SGR
RET
;------------------------------------------------;
SET_MODE: MOV AH,ON ;Assume command 7,
JMP SHORT CK_WRAP ; turn line wrap on.
;------------------------------------------------;
RESET_MODE: MOV AH,OFF ;Assume turn line wrap off.
CK_WRAP: MOV AL,BYTE PTR NUMBER_BUFFER ;Retrieve number parameter.
CMP AL,7 ;Is it 7?
JZ SET_WRAP ;If yes, set wrap mode.
JB DO_MODE ;1 - 6 valid modes.
CMP AL,14 ;14 - 16 valid modes.
JB MODE_END
CMP AL,19
JA MODE_END ;If above 16, illegal.
DO_MODE: XOR AH,AH ;Else, set video mode
INT 10H ; to parameter.
RET
SET_WRAP: MOV LINE_WRAP,AH
MODE_END: RET
;--------------------------------------------------------------------;
; Keyboard Key Reassignment: First convert ASCII numbers to binary, ;
; parse out delimiting semi-colons, and quote string delimiters. ;
;--------------------------------------------------------------------;
REASSIGNMENT: TEST STATUS,POFF ;:If assignment are OFF,
JZ NOT_POFF ;: then exit as if
JMP ASSIGN_FLUSH ;: we are out of room.
NOT_POFF: ;:
MOV CX,ESC_COUNT ;Retrieve length of Esc sequence.
DEC CX ;Adjust.
MOV SI,OFFSET ESC_BUFFER + 2 ;Point past Esc, bracket.
MOV DI,OFFSET PARSE_BUFFER ;Point to parse storage.
NEXT_STRING: MOV QUOTE_TYPE,0 ;Reset quote type.
NEXT_NUM: XOR DL,DL ;Use DL to carry number; init = 0
XOR BP,BP ;Use BP as number flag.
NEXT_PARAM: LODSB ;Retrieve a byte.
DEC CX ;Decrement string length counter.
JZ PARSE_END ;If zero, done.
MOV AH,QUOTE_TYPE ;Else, retrieve quote type.
OR AH,AH ;If zero, not in string.
JNZ QUOTE_MODE ;Else, go to string mode.
CALL CK_QUOTE ;Is character a quote?
JZ NEXT_PARAM ;If yes, ignore.
CMP AL,';' ;Else, is it semi-colon?
JZ STORE_NUMBER ;If yes, number delimiter.
SUB AL,'0' ;Else, must be number; convert
MOV DH,AL ; to binary; save in DH.
MOV AX,10 ;Multiply current total by ten.
MUL DL
ADD AL,DH ;Add new number.
MOV DL,AL ;Save in DL.
MOV BP,1 ;Flag that number mode active.
JMP SHORT NEXT_PARAM ;Next parameter.
STORE_NUMBER: OR BP,BP ;If number mode flag not set then
JZ NEXT_PARAM ;semi-colon not prefaced with no.
MOV AL,DL ;Else, store number.
STOSB
JMP SHORT NEXT_NUM ;Reset number carrying registers.
QUOTE_MODE: CMP AL,AH ;Is current char a string ending
JZ NEXT_STRING ; quote? If yes, exit quote mode
STOSB ;Else, store char as literal.
JMP SHORT NEXT_PARAM
;----------------------------------------------------------------------------;
; Remove duplicate assignments if removal leaves room for new assignment. ;
; If no duplicate and room, add new assignment, else flush buffer to screen. ;
;----------------------------------------------------------------------------;
PARSE_END: OR BP,BP ;Was last parameter a number?
JZ CK_LENGTH ;If no, skip.
MOV AL,DL ;Else, store the last number.
STOSB
CK_LENGTH: SUB DI,OFFSET PARSE_BUFFER - 2 ;Calculate string length
MOV AX,DI ; including word for length.
MOV BX,WORD PTR PARSE_BUFFER ;BL=new first ASCII; BH=2nd.
MOV CX,4 ;String length has to be at
OR BL,BL ;+least word + 2 for old = 4
JZ CK_LEGAL ; for extended key reassignment.
DEC CX ;And word + 1 for old = 3
CK_LEGAL: CMP AX,CX ; for regular ASCII reassignment.
JB ASSIGN_FLUSH ;If not, illegal; flush buffer.
JNE DOCHK ;+If equal then unassign.
XOR AX,AX ;+
DOCHK: MOV BP,BUFFER_END ;BP to carry buffer end pointer.
CALL CK_MATCH ;Is char already reassigned?
JC CK_ROOM ;If no, check room for new.
CK_REMOVE: MOV CX,DX ;REASSIGN_END - string end
SUB CX,SI ; = bytes to move.
JZ CK_ROOM ;If zero, last string.
SUB DX,[DI] ;Is REASSIGN_END - old string
ADD DX,AX ; + new string >
CMP DX,BP ; BUFFER_END?
JA ASSIGN_FLUSH ;If yes, flush buffer to screen.
REP MOVSB ;Else, move them down in buffer.
JMP SHORT STORE_NEW
CK_ROOM: ADD DX,AX ;New string + current strings.
CMP DX,BP ;Greater than buffer size?
JA ASSIGN_FLUSH ;If yes, flush new string.
STORE_NEW: MOV SI,OFFSET PARSE_BUFFER ;Else, room for new string.
TEST AX,255 ;+Any reassign?
JZ NOREASG ;+no reassignment
STOSW ;Store string length first.
MOV CX,AX ;Adjust counter.
DEC CX
DEC CX
REP MOVSB ;Store the actual string.
NOREASG: MOV REASSIGN_END,DI ;+Store new string end.
RET
ASSIGN_FLUSH: CMP AX,2 ;+Any key number?
JNE FLUSHIN ;+no
MOV AX,OFFSET REASSIGNMENT_BUFFER
MOV REASSIGN_END,AX ;+flush all reassigns.
RET ;+
FLUSHIN: MOV AL,'p' ;+If buffer full, flush string
JMP FLUSH_BUFFER ; including ending 'p' command.
;------------------------------------------------------------------------------;
; INPUT: BL = first ASCII code to match; BH = extended if BL = 0 ;
; OUTPUT: CY = 1 if no match found; CY = 0 if match found. ;
; DI points to string length of match; DI+2 points to start of string. ;
; SI = end of string; DX = REASSIGN_END; BP = BUFFER_END ;
;------------------------------------------------------------------------------;
CK_MATCH: MOV DX,REASSIGN_END ;Current strings end.
MOV SI,OFFSET REASSIGNMENT_BUFFER ;Point to buffer.
NEXT_MATCH: MOV DI,SI ;Current record.
CMP DI,DX ;End of strings?
JAE NO_MATCH ;If yes, no match.
MOV CX,[DI+2] ;CL=current first ASCII; CH=2nd
ADD SI,[DI] ;Point to next record.
CMP BL,CL ;First characters match?
JNZ NEXT_MATCH ;If no, check next record.
OR BL,BL ;Extended code? ie zero.
JNZ MATCH ;If no, then match.
CMP BH,CH ;Else, check second code.
JNZ NEXT_MATCH ;If not same, no match.
MATCH: CLC ;Else return with CY = 0
RET
NO_MATCH: STC ;No match; CY = 1.
RET
;----------------------------------------------------------------------;
; Buffer area will write over disposable data and initialization code. ;
;----------------------------------------------------------------------;
ESC_BUFFER = $
NUMBER_BUFFER = ESC_BUFFER + ESC_BUFFER_SIZE
PARSE_BUFFER = NUMBER_BUFFER
DEVICE_STATUS_BUFFER = PARSE_BUFFER + ESC_BUFFER_SIZE
DEVICE_STATUS_SIZE = 11
REASSIGNMENT_BUFFER = DEVICE_STATUS_BUFFER + DEVICE_STATUS_SIZE
; DISPOSABLE DATA
; ---------------
SYNTAX LABEL BYTE
DB 'Syntax: ANSI [FAST | SLOW][ON | OFF][KON | KOFF][PON | POFF]' ;^
if PCB ;^
DB '[XON | XOFF]' ;^
endif ;^
DB CR,LF ;: ;^
DB ' [/B nnn][/C][/Q][/U][/T][/S][/P n]',CR,LF ;. ;@ ;:
DB 'FAST = direct screen writes; default',CR,LF ;:
DB 'SLOW = screen writes via BIOS',CR,LF ;:
DB 'ON/OFF = active/inactive; default is ON',CR,LF ;:
DB 'KON/KOFF = active/inactive reassignments; default is ON',CR,LF ;:
DB 'PON/POFF = active/inactive NEW reassignments; default is ON',CR,LF ;:
if PCB ;^
DB 'XON/XOFF = active/inactive @Xnn color codes; default is ON',CR,LF ;^
endif ;^
DB 'nnn = buffer size in bytes (0 - 60K) reserved for key reassignment; '
DB 'default 200',CR,LF
DB '/Q = Quiet, no output when executed',CR,LF ;:
DB '/U = Uninstall',CR,LF ;:
DB '/T = Test if loaded',CR,LF ;$
if PCB ;$
DB '/S = Load stats from ANSICOM.SYS',CR,LF ;@
endif ;$
DB '/Pn = Protect n lines from scrolling at top' ;.
CR_LF DB CR,LF,LF,'$'
STATUS_MSG DB 'Status: $'
BUFFER_MSG DB CR,LF,'Buffer size: $'
BYTES_FREE DB CR,LF,'Bytes free: $'
ANSI_SYS_MSG DB 'ANSI.SYS is installed so '
NOT_INSTALLED DB 'ANSI.COM not installed',CR,LF,'$'
CON DB 'CON'
CON_OFFSET EQU 10
QUIET DB 0 ;| Quiet Mode
SYNTAX_FLAG DB 0 ;; Syntax Display Flag
TEST_FLAG DW 0 ;^ For report use
SIZE_MSG DB 'Uninstall to change buffer size',CR,LF,LF,'$'
UNLOAD_MSG DB 'ANSI can not be uninstalled',CR,LF
DB 'Uninstall resident programs in reverse order',CR,LF,'$'
NOT_ENOUGH DB 'Not enough memory',CR,LF,'$'
ALLOCATE_MSG DB 'Memory allocation error',CR,LF,BELL,'$'
INSTALL_MSG DB 'Installed',CR,LF,'$'
UNINSTALL_MSG DB 'Uninstalled',CR,LF,'$'
if PCB ;$
NO_FILE_MSG DB 'ANSICOM.SYS not found',CR,LF,'$' ;@
FILE_ERROR_MSG DB 'Error reading ANSICOM.SYS',CR,LF,'$' ;@
FILE_NAME DB 'ANSICOM.SYS',0 ;@
endif ;$
alrdy_installed db 0 ;^ Installed flag
other_seg dw 0 ;^ Segment of installed code
errmsg0 db 'Need DOS 2.0 or greater!$' ;^
errmsg14 db 'Error using Int 2Fh!$' ;^
INITIALIZE PROC NEAR
;--------------------------------------------------------------------;
; Search memory for a copy of our code, to see if already installed. ;
;--------------------------------------------------------------------;
cld ;^
mov ah,30h ;^ Get DOS version
int 21h ;^
xchg al,ah ;^ Swap major, minor numbers
mov dx,offset errmsg0 ;^ Bad DOS version
cmp ah,2 ;^ Run if DOS 2.0 or greater.
jb jmp_msg_exit ;^
mov dos_version,ax ;^ Save version number
;^
;^ See if a copy is already resident in memory. If > DOS 3.0, use int 2Fh.
;^
mov byte ptr [start+2],0 ;^ Initialize fingerprint
cmp dos_version,300h ;^ See if DOS 3.0 or later
jb find_copy1 ;^ No, search the old way.
mov cx,16 ;^ Try 16 different IDs.
find_copy: ;^
xor ax,ax ;^
mov es,ax ;^
mov ah,multiplex_id ;^ Load ID. Use Int 2Fh to
int 2fh ;^ reach installed code so
or al,al ;^ that we are compatible
jne find_copy0 ;^ with 386 memory managers.
push cs ;^
pop es ;^ If AL not changed, ALIAS not
jmp short find_copy4 ;^ installed.
find_copy0: ;^
push cx ;^
call cmpheader ;^ See if really Alias by
pop cx ;^ comparing file headers.
je find_copy3 ;^
inc multiplex_id ;^ ID used by another program.
loop find_copy ;^ Change and try again.
mov dx,offset errmsg14 ;^ All IDs taken, print error
jmp_msg_exit: jmp msg_exit ;^ msg and exit.
;
;^ For DOS 2.x find the installed code the old fashioned way by scanning
;^ the memory control blocks.
;^
find_copy1: ;^
xor bx,bx ;^ zero BX for start
mov ax,cs ;^ keep CS value in AX
find_copy2: ;^
inc bx ;^ increment search segment value
mov es,bx ;^
assume es:nothing ;^
cmp ax,bx ;^ not installed if current
je find_copy4 ;^ segment is found.
call cmpheader ;^
jne find_copy2 ;^ loop back if not found
find_copy3: ;^
inc alrdy_installed ;^ Set installed flag
find_copy4: ;^
mov other_seg,es ;^ Save seg of installed code
;^ CLD ;All string operations forward.
;^ MOV BX,OFFSET START ;Point to start of code.
;^ NOT BYTE PTR [BX] ;Change a byte so no false match.
;^ XOR DX,DX ;Start at segment zero.
;^ MOV AX,CS ;Store our segment in AX.
;^ NEXT_PARAG: INC DX ;Next paragraph.
;^ MOV ES,DX
;^ CMP DX,AX ;Is it our segment?
;^ JZ ANNOUNCE ;If yes, search is done.
;^ MOV SI,BX ;Else, point to our signature.
;^ MOV DI,BX ; and offset of possible match.
;^ MOV CX,16 ;Check 16 bytes for match.
;^ REP CMPSB
;^ JNZ NEXT_PARAG ;If no match, keep looking.
;------------------------------------------------;
ANNOUNCE: ;|
MOV SI,81H ;Point to command line.
NEXT_CAP: LODSB ;Capitalize parameters.
CMP AL,CR
JZ PARSE
CMP AL,'a'
JB NEXT_CAP
CMP AL,'z'
JA NEXT_CAP
AND BYTE PTR [SI - 1],5FH
JMP SHORT NEXT_CAP
;------------------------------------------------;
PARSE: MOV SI,81H ;Point to command line again.
NEXT_PARA: XOR AX,AX ;Position in status parameters.
MOV BX,4 ;Status parameters each 4 bytes.
NEXT_STATUS: MOV DI,OFFSET PARAMETERS ;Point to 'OFF ON SLOWFAST'
ADD DI,AX ;Point to next parameter.
ADD AX,BX
CMP AX,LAST_PARAMETER ;:Check all 8 possible statuses
JA CK_SWITCHES
PUSH SI ;Save command line pointer.
MOV CX,2 ;Check first two bytes for match.
CMP AX,20 ;: 3 Bytes for
JB Len_OK ;: KON | KOFF | PON | POFF
INC CX ;: ;^ XON | XOFF
Len_OK: ;:
REP CMPSB
POP SI ;Recover command line pointer.
JNZ NEXT_STATUS
DIV BL ;If match, divide offset by four.
;:
;: Next 12 lines of routine changed
;:
;: MOV CL,1 ;Set a bit to match offset.
;:NEXT_SHIFT: SHL CL,1
;: DEC AL
;: JNZ NEXT_SHIFT
;: SHR CL,1 ;Adjust.
;: MOV AH,STATUS_MASK ;Retrieve appropriate ON/OFF
;: CMP CL,ON ; or FAST/SLOW mask.
;: JBE ADD_STATUS
;: ROL AH,1
;: ROL AH,1
;:
;:ADD_STATUS: AND ES:STATUS,AH ;Mask off old status.
;: OR ES:STATUS,CL ;Add new status.
;:
MOV CL,AL ;: ;^ place count in CL (1 - 10)
PUSH DX ;^ Save Segment
MOV AX,AND_MASK ;: ;^ Retrieve appropriate ON/OFF
;: FAST|SLOW|KON|KOFF|PON|POFF
MOV DX,OR_MASK ;^ XON|XOFF
DEC CL ;: ;^ Make 1 - 10 into 0 - 9
JZ Skip_1 ;: In position already if zero
SHL DX,CL ;: ;^ Shift bit into position
Skip_1: SHR CL,1 ;: divide count by 2
JZ Skip_2 ;: Nevermind
ROL AX,CL ;: ;^ Shift Mask into position
ROL AX,CL ;: ;^
Skip_2: ;:
AND ES:STATUS,AX ;^ Mask off old status.
OR ES:STATUS,DX ;: ;^ Add new status.
POP DX ;^ get back segment
INC SI ;Bump command line pointer.
INC SI
CK_SWITCHES: LODSB ;Get a byte.
CMP AL,CR ;Is it carriage return?
JNE QUES ;$
JMP INSTALL ;If yes, done here.
QUES: CMP AL,'?' ;;Request for Syntax?
JNE CK_SW ;; No -
MOV BYTE PTR SYNTAX_FLAG, 1 ;; Yes-Show Syntax
CK_SW: CMP AL,'/' ;;Is there a switch character?
JNE NEXT_PARA ;;If no, keep looking.
GOT_SWITCH: LODSB ;;Else, get the switch character.
CMP AL,'B' ;Is it 'B' ?
JNZ CK_C ;If no, check 'C'.
CALL CK_INSTALLED ;Else, see if already installed.
JZ GET_SIZE ;If no, get buffer request size.
MOV DX,OFFSET SIZE_MSG ;Else, display error message.
CALL PRINT_STRING
NEXT_PARA2: JMP SHORT NEXT_PARA
GET_SIZE: CALL DECIMAL_INPUT ;Get number parameter.
CMP BX,REASSIGNMENT_MAX ;If greater than maximum, use
JBE STORE_BUFFER ; maximum, else use requested
MOV BX,REASSIGNMENT_MAX ; size.
STORE_BUFFER: MOV REASSIGNMENT_SIZE,BX
ADD BX,OFFSET REASSIGNMENT_BUFFER ;Calculate end of buffer.
MOV BUFFER_END,BX
JMP NEXT_PARA ;;Too far for direct jump
CK_C: CMP AL,'C' ;Is it 'C' ?
if PCB ;$
JNZ CK_S ;@ If not, check 'S'.
else ;$
JNZ CK_P ;$ If not, check 'P'.
endif ;$
MOV ES:REASSIGN_END,OFFSET REASSIGNMENT_BUFFER ;Clear buffer
JMP NEXT_PARA ;Else, next parmater.
if PCB ;$
CK_S: CMP AL,'S' ;@ Is It 'S' ?
JNZ CK_P ;. If not, check 'P'.
CALL LOAD_STATS ;@ Yes, Load Stats
JMP NEXT_PARA2 ;@ Next parameter (indirect)
endif ;$
CK_P: CMP AL,'P' ;. Is it 'P' ?
JNZ CK_Q ;|If not, check 'Q'.
CMP BYTE PTR [SI], '*' ;$Use current row-1?
JNE CK_PN ;$no
PUSH SI ;$Get
CALL GET_BIOS_DATA ;$ current
MOV BL, ES:CURSOR_ROW ;$ row
DEC BL ;$ -1
POP SI ;$
JMP CK_PS ;$
CK_PN: CALL DECIMAL_INPUT ;. Get number of lines
CK_PS: MOV ES:START_SCROLL,BL ;. Store the value
JMP NEXT_PARA2 ;. Next parameter (indirect)
CK_Q: CMP AL,'Q' ;|See if Quiet Mode
JNE CK_U ;|If not, check 'U'.
NOT QUIET ;|If yes turn on Quiet
JMP NEXT_PARA ;Else, next parmater.
CK_U: CMP AL,'U' ;Is it 'U' ?
JE DO_U ;If yes, try to uninstall.
CMP AL,'T' ;; Test if Installed
JE DO_U
MOV BYTE PTR SYNTAX_FLAG, 1 ;; Syntax Error
JMP NEXT_PARA ;Else, next parmater.
;------------------------------------------------;
INSTALL:
MOV DX,OFFSET SIGNATURE ;|Display our signature.
CALL PRINT_STRING ;|
CMP BYTE PTR SYNTAX_FLAG,0 ;;Display Syntax?
JE SKIP_SYNTAX ;;No
MOV DX,OFFSET SYNTAX ;|Yes, Display syntax.
CALL PRINT_STRING ;|
SKIP_SYNTAX: CALL STATUS_REPORT ;;|Display status.
CALL CK_INSTALLED ;Check if already installed.
JZ CK_AVAILABLE ;If no, see if enough memory.
XOR AL,AL ;;Else, done.
EXIT2: JMP EXIT ;Exit with ERRORLEVEL = 0.
DO_U: CALL CK_INSTALLED ;Else, see if installed.
MOV DX,OFFSET NOT_INSTALLED ;If no, exit with error message.
JZ LILLY_PAD ;Too far for short jump.
CMP AL,'T' ;;Was it a test
MOV AX, STATUS ;$return switches as ;^
JE EXIT2 ;;$Yes Too far for short jump.
JMP UNINSTALL ;$Else, uninstall.
;--------------------------------;
; This is the install procedure. ;
;--------------------------------;
CK_AVAILABLE: MOV BP,OFFSET REASSIGNMENT_BUFFER ;TSR ends at end
ADD BP,REASSIGNMENT_SIZE ; of reassignment buffer.
ADD BP,15 ;Round up.
CMP BP,DS:[6] ;Buffer > PSP bytes in segment?
MOV DX,OFFSET NOT_ENOUGH ;If yes, exit without installing
JBE CK_ANSI ;@
LILLY_PAD: JMP MSG_EXIT ;@with message
CK_ANSI: MOV AX,3529H ;@Get undocumented INT 29 vector.
INT 21H
MOV SI,OFFSET CON ;Does it point to ANSI.SYS?
MOV DI,CON_OFFSET ;Check by looking for 'CON'
MOV CX,3 ; as device name.
REP CMPSB
MOV DX,OFFSET ANSI_SYS_MSG ;Exit with error message if
JZ LILLY_PAD ;ANSI.SYS loaded.
MOV OLD_INT_29[0],BX ;Else save old interrupt.
MOV OLD_INT_29[2],ES
MOV DX,OFFSET ANSI_INT_29 ;Install new interrupt.
MOV AX,2529H
INT 21H
MOV AX,3516H ;Get INT 16 vector.
INT 21H
MOV OLD_INT_16[0],BX ;Save old interrupt.
MOV OLD_INT_16[2],ES
MOV DX,OFFSET ANSI_INT_16 ;Install new interrupt.
MOV AX,2516H
INT 21H
MOV AX,3521H ;Get DOS 21h interrupt.
INT 21H
MOV OLD_INT_21[0],BX ;Save old interrupt.
MOV OLD_INT_21[2],ES
MOV DX,OFFSET ANSI_INT_21 ;Install new interrupt.
MOV AX,2521H
INT 21H
if PCB ;$
MOV AX,3508H ;@ Get INT 08 vector.
INT 21H ;@
MOV OLD_INT_08[0],BX ;@ Save old interrupt.
MOV OLD_INT_08[2],ES ;@
MOV DX,OFFSET ANSI_INT_08 ;@ Install new interrupt.
MOV AX,2508H ;@
INT 21H ;@
endif ;$
cmp dos_version,300h ;^ See if we are using Int 2Fh
jb install_3 ;^
mov ax,352fh ;^ Get interrupt 2F (MUX)
int 21h ;^ vector.
mov word ptr [int2fh],bx ;^
mov word ptr [int2fh+2],es ;^
mov ax,252fh ;^ Point int 2F to internal
mov dx,offset muxint ;^ routine.
int 21h
Install_3: ;^
MOV AX,DS:[2CH] ;Get environment segment.
MOV ES,AX
MOV AH,49H ;Free up environment.
INT 21H
MOV DX,OFFSET INSTALL_MSG ;Display install message.
CALL PRINT_STRING
CALL FLUSH_END ;Setup the number buffer.
MOV DX,BP ;Retrieve resident byte request.
MOV CL,4
SHR DX,CL ;Convert to paragraphs.
MOV AX,3100H ;Return error code of zero.
INT 21H ;Terminate but stay resident.
;-------------------------------------------------------------------;
; Exit. Return ERRORLEVEL code 0 if successful, 1 if unsuccessful. ;
;-------------------------------------------------------------------;
MSG_EXIT: CALL PRINT_STRING
ERROR_EXIT: MOV AL,1 ;ERRORLEVEL = 1.
EXIT: MOV AH,4CH ;Terminate.
INT 21H
;^-----------------------------------------------------------------------------
;^ CMPHEADER compares the first 16 bytes of this file with the segment
;^ pointed to by ES.
;^ Entry: DS - code segment
;^ ES - pointer to segment to compare
;^ Exit: ZF - 0 = segments match.
;^-----------------------------------------------------------------------------
cmpheader proc near ;^
mov si,offset start+2 ;^ Search this segment for ASCII
mov di,si ;^ fingerprint.
mov cx,16 ;^
repe cmpsb ;^
ret ;^
cmpheader endp ;^
;---------------------------------------------------;
; This subroutine uninstalls the resident ANSI.COM. ;
;---------------------------------------------------;
UNINSTALL: AND ES:STATUS,NOT ON ;Turn OFF ANSI, just in case
OR ES:STATUS,OFF ; can't uninstall.
MOV CX,ES ;Save segment in CX.
MOV AX,3529H ;Get interrupt 29h.
INT 21H
CMP BX,OFFSET ANSI_INT_29 ;Has it been hooked by another?
JNZ UNINSTALL_ERR2 ;@If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR2 ;@If no, exit with error message.
if PCB ;$
MOV AX,3508H ;@Get interrupt 08h.
INT 21H ;@
CMP BX,OFFSET ANSI_INT_08 ;@Has it been hooked by another?
JNZ UNINSTALL_ERR2 ;^;@If yes, exit with error message.
MOV BX,ES ;@
CMP BX,CX ;@Is the segment vector same?
JNZ UNINSTALL_ERR2 ;^;@If no, exit with error message.
endif ;$
MOV AX,3516H ;Get interrupt 16h.
INT 21H
CMP BX,OFFSET ANSI_INT_16 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
UNINSTALL_ERR2:JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AX,3521H ;Get interrupt 21h.
INT 21H
CMP BX,OFFSET ANSI_INT_21 ;Has it been hooked by another?
JNZ UNINSTALL_ERR ;If yes, exit with error message.
MOV BX,ES
CMP BX,CX ;Is the segment vector same?
JNZ UNINSTALL_ERR ;If no, exit with error message.
MOV AH,49H ;Return memory to system pool.
INT 21H
MOV DX,OFFSET ALLOCATE_MSG
JC MSG_EXIT ;Display message if problem.
lds dx,es:[int2fh] ;^ Get old interrupt 2F vector
cmp dx,-1 ;^ See if Int used
je remove_1 ;^ No, skip check of Int 2F
mov ax,252fh ;^ Set interrupt
int 21h ;^
remove_1: ;^
MOV DX,ES:OLD_INT_29[0] ;Restore old INT 29.
MOV DS,ES:OLD_INT_29[2]
MOV AX,2529H
INT 21H
if PCB ;$
MOV DX,ES:OLD_INT_08[0] ;@Restore old INT 08.
MOV DS,ES:OLD_INT_08[2] ;@
MOV AX,2508H ;@
INT 21H ;@
endif ;$
MOV DX,ES:OLD_INT_16[0] ;Restore old INT 16.
MOV DS,ES:OLD_INT_16[2]
MOV AX,2516H
INT 21H
MOV DX,ES:OLD_INT_21[0] ;Restore old INT 21.
MOV DS,ES:OLD_INT_21[2]
MOV AX,2521H
INT 21H
PUSH CS
POP DS ;Point to our data.
MOV DX,OFFSET UNINSTALL_MSG ;Display uninstall message.
CALL PRINT_STRING
OR AL,AL ;Exit with ERRORLEVEL = 0.
JMP EXIT
UNINSTALL_ERR: MOV ES,CX ;If error, display OFF status.
CALL STATUS_REPORT
MOV DX,OFFSET UNLOAD_MSG ;And exit with error message.
JMP MSG_EXIT
INITIALIZE ENDP
;--------------------------------------------------;
; INPUT: SI points to parameter start. ;
; OUTPUT: SI points to parameter end; BX = number. ;
;--------------------------------------------------;
DECIMAL_INPUT PROC NEAR
XOR BX,BX ;Start with zero as number.
NEXT_DECIMAL: LODSB ;Get a character.
CMP AL,CR ;Carriage return?
JZ ADJUST_DEC ;If yes, done here.
CMP AL,'/' ;Forward slash?
JZ ADJUST_DEC ;If yes, done here.
SUB AL,'0' ;ASCII to binary.
JC NEXT_DECIMAL ;If not between 0 and 9, skip.
CMP AL,9
JA NEXT_DECIMAL
CBW ;Convert byte to word.
XCHG AX,BX ;Swap old and new number.
MOV CX,10 ;Shift to left by multiplying
MUL CX ; last entry by ten.
JC DECIMAL_ERROR ;If carry, too big.
ADD BX,AX ;Add new number and store in BX.
JNC NEXT_DECIMAL ;If not carry, next number.
DECIMAL_ERROR: MOV BX,-1 ;Else, too big; return -1.
ADJUST_DEC: DEC SI ;Adjust pointer.
RET
DECIMAL_INPUT ENDP
;-------------------------------------------------------;
; OUTPUT: ZR = 1 if not installed; ZR = 0 if installed. ;
;-------------------------------------------------------;
CK_INSTALLED: PUSH AX ;;
MOV AX,ES
MOV BX,CS
CMP AX,BX ;Compare segments.
POP AX ;;
RET
;------------------------------------------------;
STATUS_REPORT: MOV DX,OFFSET STATUS_MSG
CALL PRINT_STRING ;Display 'Status: '.
MOV BX,ES:STATUS ;^ Status is now a Word
MOV TEST_FLAG,1 ;^ re-ran out of registers
NEXT_REPORT: TEST BX,TEST_FLAG ;^ Check this status flag
JZ LOOP_STATUS
MOV DX,TEST_FLAG ;^ We need a copy to play with
;:
;: Next 9 Lines changed
;:
;: MOV AX,-1
;:NEXT_BIT: INC AX
;: SHR DL,1
;: JNC NEXT_BIT
;: SHL AX,1
;: SHL AX,1
;: MOV SI,OFFSET PARAMETERS ;Display appropriate ON or OFF,
;: ADD SI,AX ; SLOW or FAST.
;: MOV CX,4
;:
MOV SI,OFFSET PARAMETERS ;:Display appropriate ON or OFF,
;: SLOW|FAST. KON|KOFF|PON|POFF
MOV CX,4 ;:Each four characters long
NEXT_BIT: SHR DX,1 ;: ;^Is this our Word?
JC REPORT ;:Yes - Print it
ADD SI,CX ;:No - Skip over it
JMP NEXT_BIT ;:test the next bit
REPORT: LODSB
CALL PRINT_CHAR
LOOP REPORT
MOV AL, ' ' ;: Space words
CALL PRINT_CHAR ;:
LOOP_STATUS: SHL TEST_FLAG,1 ;^
TEST TEST_FLAG,03FFH ;^
JNZ NEXT_REPORT ;: ;^ Check all 10 bits
;:
MOV DX,OFFSET BUFFER_MSG ;Display 'Buffer size: '.
CALL PRINT_STRING
MOV AX,ES:REASSIGNMENT_SIZE
PUSH AX
XOR BP,BP
CALL DECIMAL_OUT ;Display the size.
MOV DX,OFFSET BYTES_FREE ;Display 'Bytes free: '.
CALL PRINT_STRING
POP AX
ADD AX,OFFSET REASSIGNMENT_BUFFER
SUB AX,ES:REASSIGN_END
CALL DECIMAL_OUT ;Display the bytes free.
MOV DX,OFFSET CR_LF
CALL PRINT_STRING
RET
;------------------------------------------------;
PRINT_CHAR: MOV DL,AL
MOV AH,2 ;Print character via DOS.
OR AL,AL ;:Skip NULLs
JNZ SHORT DOS_INT ;:
RET ;:
PRINT_STRING: MOV AH,9 ;Print string via DOS.
DOS_INT: CMP QUIET,0 ;|Do we suppress output
JNE NO_PRINT ;|Yes
INT 21H ;|No
NO_PRINT: ;|
RET
if PCB ;$
LOAD_STATS: MOV DX,OFFSET FILE_NAME ;@ Stat filename
MOV AX,3D00H ;@ Open File
INT 21H ;@ DOS Interupt
MOV DX,OFFSET NO_FILE_MSG ;@ Assume no file
JNC OPEN_OK ;@ Ok, So far
MSG_EXIT2: JMP MSG_EXIT ;@ No file found probably
OPEN_OK: MOV BX,AX ;@ This is our Handle number
PUSH DS ;@ Save DS
PUSH ES ;@ Set ES to DS
POP DS ;@
MOV DX, OFFSET SUB_LIST ;@ Point to the Sub_List
MOV CX, SUB_LENGTH ;@ Get the Length ot the List
MOV AH,3FH ;@ Read from Handle
INT 21H ;@ DOS Interupt
POP DS ;@ Restore DS
PUSHF ;@ Save results
PUSH AX ;@
MOV AH,3EH ;@ Close Handle
INT 21H ;@ DOS Interupt
POP AX ;@ Restore results
POPF ;@
MOV DX,OFFSET FILE_ERROR_MSG ;@ Assume an error occured
JC MSG_EXIT2 ;@ Yep, it did
XCHG AX,CX ;@ Swap Bytes Read
JCXZ MSG_EXIT2 ;@ Something went wrong
CMP AX,CX ;@ Did we read the full amount
JNE MSG_EXIT2 ;@ No
RET ;@ All went well!
endif ;$
_TEXT ENDS
END START